<?php

namespace app\service;

use app\exception\UserException;
use app\model\AdminUser;
use app\utils\Jwt;
use think\facade\Cache;
class AdminUserService
{
    private $adminUser;
    private $loginService;
    //创建构造方法
    public function __construct()
    {
        $this->adminUser = new AdminUser;
        $this->loginService = new LoginService;
    }

    public function list($params = [])
    {
        $page = $params['page'] ?? 1;
        $pageSize = $params['pageSize'] ?? 10;

        $adminUser = $this->adminUser;

        $where = [];

        if(!empty($params['username'])) $where[] = ['username', 'like', '%'.$params['username'].'%'];

        if(isset($params['status']) && $params['status']!='' ) $where[]=['status','=',$params['status']];

        $result = $adminUser->where($where)->order(['updated_at' => 'desc', 'id' => 'desc'])->paginate([
            'list_rows'=> $pageSize,
            'page' => $page
        ])->hidden(['password'])->append(['sex_text','status_text']);

        return $result;
    }

    /**
     * 保存管理员用户信息
     *
     * @param array $param 包含用户信息的数组，应包括username, password, qpassword, phone等键
     * @return mixed 插入成功后的结果
     *
     * 此函数用于接收一个包含用户信息的数组，验证信息的完整性及正确性，
     * 并将用户信息保存到数据库中。如果用户名或密码为空，或者密码长度不符合要求，
     * 或者两次输入的密码不一致，则会终止操作并返回相应的错误信息。如果用户名
     * 或手机号已存在，也会终止操作并返回错误信息。密码在保存前会被加密处理。
     */
    public function save($param)
    {
        // 提取并默认为空字符串的用户输入数据
        $username = $param['username'] ?? '';
        $password = $param['password'] ?? '';
        $confirmPassword = $param['confirmPassword'] ?? '';
        $phone = $param['phone'] ?? '';

        // 检查用户名是否为空
        if(empty($username)) throw new UserException('用户名不能为空');

        // 检查密码是否为空
        if(empty($password)) throw new UserException('密码不能为空');

        //验证密码长度6-20位
        if(strlen($password)<6 || strlen($password)>20) throw new UserException( '密码长度6-20位');

        // 检查两次输入的密码是否一致
        if($password!=$confirmPassword) throw new UserException( $password.'两次密码不一致'.$confirmPassword);

        // 检查用户名或手机号是否已存在
        $adminUser = $this->adminUser->where('username', $username)->whereOr('phone', $phone)->find();
        if(!empty($adminUser)){
            if($username==$adminUser->username) throw new UserException( '用户已存在');
            if($phone==$adminUser->phone) throw new UserException( '手机号已存在');
        }

        // 对密码进行加密处理
        $param['password'] = password_hash($password, PASSWORD_DEFAULT);

        if(isset($param['id'])) unset($param['id']);

        // 允许指定字段插入，提高数据安全性
        return $this->adminUser->allowField(['username', 'password', 'phone','avatar','sex','nickname'])->create($param);
    }


    /**
     * 用户登录方法
     * 该方法用于验证用户身份，生成并返回JWT令牌
     *
     * @param array $param 包含用户名和密码的数组
     * @return array 包含用户信息和JWT令牌的数组
     */
    public function login($param)
    {
        // 提取用户名和密码
        $username = $param['username'] ?? '';
        $password = $param['password'] ?? '';

        // 检查是否超过登录尝试次数限制
        if (!$this->loginService->checkLoginAttempts($username)) {
            $remainingTime = $this->loginService->getLockoutTime($username);
            throw new UserException('登录失败次数过多，请' . ceil($remainingTime / 60) . '分钟后再试');
        }

        // 检查用户名和密码是否为空
        if (empty($username) || empty($password)) throw new UserException( '登录失败');

        // 查询用户是否存在
        $adminUser = $this->adminUser->where('username', $username)->find();

        // 用户不存在或密码不匹配
        if (!$adminUser || !password_verify($password, $adminUser->password)) {
            $this->loginService->incrementLoginAttempts($username);

            // 获取剩余尝试次数
            $remainingAttempts = $this->loginService->getRemainingAttempts($username);

            if ($remainingAttempts > 0) {
                throw new UserException('用户名或密码错误，您还有' . $remainingAttempts . '次尝试机会');
            } else {
                throw new UserException('登录失败次数过多，请10分钟后再试');
            }
        }

        // 检查用户状态是否正常
        if($adminUser->status!=1) throw new UserException( '账号异常,请联系管理员!');

        // 登录成功，清除失败记录
        $this->loginService->clearLoginAttempts($username);

        // 获取JWT配置参数
        $secret = env('jwt.admin_secret','');
        $expire = env('jwt.admin_expires',0);

        // 生成JWT令牌
        $token = Jwt::generateToken($adminUser->id,'', $adminUser->username, $secret, $expire);

        // 使用文件缓存，将令牌与用户ID关联并设置过期时间
        Cache::store('file')->set($token, $token, $expire);

        // 返回生成的JWT令牌和用户信息
        return [
            "token"=>$token,
            "username" => $adminUser->username,
            "phone"=> $adminUser->phone,
            "avatar" => $adminUser->avatar,
            "sex" => $adminUser->sex,
            "nickname"=> $adminUser->nickname
        ];
    }

    /**
     * 执行用户退出操作
     *
     * 本函数通过删除缓存中的令牌来实现用户退出登录的功能
     * 之所以选择这种方式，是因为在无状态的API设计中，服务器不存储用户的会话状态
     * 通过删除令牌，可以确保即使用户未主动注销，其登录状态也会在一定时间后失效
     *
     * @param string $token 用户的唯一令牌，用于标识用户会话
     *
     * @return bool 删除令牌的结果，成功返回true，失败返回false
     */
    public function logout($token)
    {
        return Cache::store('file')->delete($token);
    }

    public function delete($id){
        if($id==1) throw new UserException('超级管理员无法删除');
        return $this->adminUser->destroy($id);
    }

    public function update($id,$param){
        $adminUser = $this->adminUser->find($id);

        //是否有密码更新
        if(empty($param['password'])){
            unset($param['password']);
        }else{
            if(empty($param['oldpassword'])) throw new UserException('请输入旧密码');
            if(empty($param['confirmPassword'])) throw new UserException('确认密码不能为空');
            if($param['password']!=$param['confirmPassword']) throw new UserException('两次密码不一致');

            if(!password_verify($param['oldpassword'], $adminUser->password)) throw new UserException('原始密码错误');

            if($param['oldpassword']==$param['password']) throw new UserException('新密码不能与旧密码相同');

            $param['password'] = password_hash($param['password'], PASSWORD_DEFAULT);
        }

        //数据库存在的字段才更新
        return $adminUser->allowField(['phone','avatar','sex','nickname','status','password'])->save($param);
    }

    public function read($id){
        return $this->adminUser->find($id)->hidden(['password']);
    }

}
